
import Vapor
import Fluent

struct DepartmentController: RouteCollection {
  func boot(routes: RoutesBuilder) throws {
    let departmentRoutes = routes.grouped("api", "department")
    departmentRoutes.get(use: getAllHandler)
    departmentRoutes.post(use: createHandler)
    
    //Just for Migrate
    departmentRoutes.post("createWithEmployees", use: createWithEmployees)

    departmentRoutes.get(":departmentID", use: getHandler)
    departmentRoutes.put(":departmentID", use: updateHandler)
    departmentRoutes.delete(":departmentID", use: deleteHandler)
    departmentRoutes.post(":departmentID", "employee", ":employeeID", use: addEmployeesHandler)
    departmentRoutes.get(":departmentID", "employee", use: getEmployeesHandler)
    departmentRoutes.delete(":departmentID", "employee", ":employeeID", use: removeEmployeesHandler)
  }
  
  func getAllHandler(_ req: Request) throws -> EventLoopFuture<[Department]> {
        Department.query(on: req.db).all()
  }
    //Just for Migrate
    func createWithEmployees(_ req: Request) throws -> EventLoopFuture<HTTPStatus> {
        let data = try req.content.decode(DepartmentData.self)
        let department = Department()
        department.id = data.id
        department.name = data.name
        let employeeFuture = Employee.query(on: req.db).all()

        return department.save(on: req.db).and(employeeFuture).flatMap { (_, employees) in
           let toEmployees = employees.filter { (emp) -> Bool in
                return data.employees.contains(emp.abbreviation)
            }
            return department.$employees.attach(toEmployees, on: req.db).map {
                HTTPStatus.created
            }
        }
    }
  
  func createHandler(_ req: Request) throws -> EventLoopFuture<Department> {
    let data = try req.content.decode(Department.self)
    return data.save(on: req.db).map { data }
  }

  func getHandler(_ req: Request) throws -> EventLoopFuture<Department> {
    Department.find(req.parameters.get("departmentID"), on: req.db)
    .unwrap(or: Abort(.notFound))
  }

  func updateHandler(_ req: Request) throws -> EventLoopFuture<Department> {
    let updateData = try req.content.decode(Department.self)
    return Department.find(req.parameters.get("departmentID"), on: req.db)
      .unwrap(or: Abort(.notFound)).flatMap { department in
        department.name = updateData.name
        return department.save(on: req.db).map {
            department
        }
    }
  }

  func deleteHandler(_ req: Request)
    throws -> EventLoopFuture<HTTPStatus> {
    Department.find(req.parameters.get("departmentID"), on: req.db)
      .unwrap(or: Abort(.notFound))
      .flatMap { department in
        department.delete(on: req.db)
          .transform(to: .noContent)
    }
  }
  
  func addEmployeesHandler(_ req: Request) throws -> EventLoopFuture<HTTPStatus> {
    let departmentQuery = Department.find(req.parameters.get("departmentID"), on: req.db).unwrap(or: Abort(.notFound))
    let employeeQuery = Employee.find(req.parameters.get("employeeID"), on: req.db).unwrap(or: Abort(.notFound))
    return departmentQuery.and(employeeQuery).flatMap { department, employee in
        department.$employees.attach(employee, on: req.db).transform(to: .created)
    }
  }
  
  func getEmployeesHandler(_ req: Request) throws -> EventLoopFuture<[Employee]> {
    Department.find(req.parameters.get("departmentID"), on: req.db)
    .unwrap(or: Abort(.notFound))
    .flatMap { department in
        department.$employees.query(on: req.db).all()
    }
  }
  
  func removeEmployeesHandler(_ req: Request) throws -> EventLoopFuture<HTTPStatus> {
    let departmentQuery = Department.find(req.parameters.get("departmentID"), on: req.db).unwrap(or: Abort(.notFound))
    let employeeQuery = Employee.find(req.parameters.get("employeeID"), on: req.db).unwrap(or: Abort(.notFound))
    return departmentQuery.and(employeeQuery).flatMap { department, employee in
        department.$employees.detach(employee, on: req.db).transform(to: .noContent)
//        department.$employees.detach(employee, on: req.db).transform(to: .noContent)
    }
  }
}

struct DepartmentData: Content {
    var id: UUID
    var name: String
    var employees: [String]
}
